package util;



/**
 * Stemmer, implementing the Porter Stemming Algorithm. The StemmerAnalyzer
 * make basis methods to Portuguese and Spanish version available.
 * The original paper is in Porter, 1980, An algorithm for suffix stripping, 
 * Program, Vol. 14, no. 3, pp 130-137,
 * See also http://www.tartarus.org/~martin/PorterStemmer
 */

public class StemmerAnalyzer {
	
	private String word = "", wordRV = "", wordAux = ""; 
	private String R1 = ""; //First rule
	private String R2 = ""; //Second rule
	private String RV = "";
	private final String vowels = "aeiou";
	
	private boolean pr12 = false;
	private boolean prv = false;
	private boolean log = false;
	
	/**
	 * @return Returns the r1.
	 */
	public String getR1() {
		return R1;
	}
	/**
	 * @param pR1 The r1 to set.
	 */
	public void setR1(String pR1) {
		R1 = pR1;
	}
	/**
	 * @return Returns the r2.
	 */
	public String getR2() {
		return R2;
	}
	/**
	 * @param pR2 The r2 to set.
	 */
	public void setR2(String pR2) {
		R2 = pR2;
	}
	/**
	 * @return Returns the rV.
	 */
	public String getRV() {
		return RV;
	}
	/**
	 * @param pRv The rV to set.
	 */
	public void setRV(String pRv) {
		RV = pRv;
	}
	/**
	 * @return Returns the word.
	 */
	public String getWord() {
		return word;
	}
	/**
	 * @return Returns the word.
	 */
	public String getWordAux() {
		return wordAux;
	}
	/**
	 * @param pWord The word to set.
	 */
	public void setWord(String pWord) {
		this.word = pWord;
		this.wordAux = pWord;
	}
	/**
	 * @return Returns the word.
	 */
	public String getWordRV() {
		return wordRV;
	}
	/**
	 * @param pWordRV The word to set.
	 */
	public void setWordRV(String pWordRV) {
		this.wordRV = pWordRV;
	}
	
	/**
	 * @return Returns the log.
	 */
	public boolean isLog() {
		return log;
	}
	/**
	 * @param pLog The log to set.
	 */
	public void setLog(boolean pLog) {
		this.log = pLog;
	}
	
	/**
	 * @return Returns the pr12.
	 */
	public boolean isPr12() {
		return pr12;
	}
	/**
	 * @param pPr12 The pr12 to set.
	 */
	public void setPr12(boolean pPr12) {
		this.pr12 = pPr12;
	}
	/**
	 * @return Returns the prv.
	 */
	public boolean isPrv() {
		return prv;
	}
	/**
	 * @param pPrv The prv to set.
	 */
	public void setPrv(boolean pPrv) {
		this.prv = pPrv;
	}
	
	/**
	 * Find the longest suffixes chain.
	 * @param pList list of suffixes.
	 * @param pRule used to search if the suffix occurs inside it.
	 * @return the initial position of the sequence.
	 * @throws Exception
	 */
	
	public int findLongest(String[] pList, String pRule) throws Exception {
		int ctr = -1, len = 0;
		for (int idx = 0; idx < pList.length; idx++) {
			if (pRule.endsWith(pList[idx])) {
				if (pList[idx].length() > len) {
					len = pList[idx].length(); ctr = idx; 
				}
			}
		}
		return ctr;
	}
	
	/**
	 * Method used to apply accentuation at the new word based on the source
	 * term.
	 * @param pStem stemmed word.
	 * @param pWord source word.
	 * @return stemmed word with accentuation.
	 * @throws Exception
	 */
	public String applyAccent(String pStem, String pWord) throws Exception {
		char[] chS = pStem.toCharArray();
        char[] chW = pWord.toCharArray();
        for (int idx = 0; idx < chS.length-1; idx++) {
        	chS[idx] = chW[idx];
        }
		if ((chS[chS.length-1] != 't') && (chS[chS.length-1] != 'T'))
    		chS[chS.length-1] = chW[chS.length-1];
        //Portuguese and Spanish rule
		final char[] cedilla = "\u00E7\u00C7".toCharArray();  
		if ((chS[chS.length-1] == cedilla[0]) || (chS[chS.length-1] == cedilla[1]))
			chS[chS.length-1] = 'c';
		return String.copyValueOf(chS);
	}

	/**
	 * Delete a suffix found at the source word.
	 * @param source source word.
	 * @param suffix suffix to be removed.
	 * @return a string without the suffix.
	 * @throws Exception
	 */
	public String delete(String source, String suffix) throws Exception {
		return source.substring(0,source.lastIndexOf(suffix));
	}
	
	/**
	 * Verify if a character is a vowel.
	 * @param pVowel input character.
	 * @return if the input is or not a vowel.
	 * @throws Exception
	 */
	private boolean isVowel(char pVowel) throws Exception {
		String aux = new Character(pVowel).toString();
		aux = Util.removeAccent(aux);
		if (vowels.indexOf(aux) != -1) //it's a vowel
			return true;
		return false;
	}

	/**
	 * Figure out rule 1 and make the content available at the getR1() method.
	 * @throws Exception
	 */
	public void rule1() throws Exception {
		try {
			int consonant = 0, idx = 0;
			boolean process = false;
			char[] ch = word.toLowerCase().toCharArray();
			if (isVowel(ch[idx]))  
				consonant++;
			while ((!process) && (ch.length > idx)) {
				if (((idx+1) < ch.length) && (!isVowel(ch[idx])) && (isVowel(ch[idx+1]))) 
					consonant++;
				else if (consonant >= 2) { 
					R1 = word.substring(idx,word.length());
					word = word.substring(0, idx);
					process = true;
				}	
				idx++;
			}
		} catch (Exception exc) {
			throw new Exception ("Context: rule1 method. Error: "+exc.getMessage());
		}
	}

	/**
	 * Figure out rule 2 and make the content available at the getR2() method.
	 * @throws Exception
	 */
	public void rule2() throws Exception {
		try {
			int consonant = 0, idx = 0;
			boolean process = false;
			if (!R1.equalsIgnoreCase("")) {
				char[] ch = R1.toLowerCase().toCharArray();
				while ((!process) && (ch.length > idx)) {
					if (idx > 0) {
						if (!isVowel(ch[idx])) 
							consonant++;
						else if (consonant >= 1) { 
							R2 = R1.substring(idx,R1.length());
							R1 = R1.substring(0,idx);
							process = true;
						}	
					}
					idx++;
				}
			}
		} catch (Exception exc) {
			throw new Exception ("Context: rule2 method. Error: "+exc.getMessage());
		}
	}
	
	/**
	 * Figure out rule v and make the content available at the getRV() method.
	 * @throws Exception
	 */
	public void ruleV() throws Exception {
		try {
			int idx = 0;
			boolean process = false;
			if (wordAux.length() > 3) { 
				char[] ch = wordAux.toLowerCase().toCharArray();
				//1st rule. 
				//If the second letter is a consonant, RV is the region after the next following vowel
				idx = 2; 
				if (!isVowel(ch[1])) {
					while ((!process) && (ch.length > idx)){
						if (isVowel(ch[idx])) {
							RV = wordAux.substring(idx+1,wordAux.length()); 
							wordRV = wordAux.substring(0, idx+1);
							process = true;
						}
						idx++;	
					}
				}
				//2sd rule
				//If the first two letters are vowels, RV is the region after the next consonant
				idx = 2;
				if (!process) {
					if ((isVowel(ch[0])) && (isVowel(ch[1]))) {
						if (!isVowel(ch[idx])) {
							RV = wordAux.substring(idx+1,wordAux.length()); 
							wordRV = wordAux.substring(0, idx+1);
							process = true;
						}
						idx++;	
					}
				}
				//3rd rule
				//if (consonant-vowel case) RV is the region after the third letter
				idx = 2; 
				if (!process) {
					if ((!isVowel(ch[0])) && (isVowel(ch[1]))) {
						RV = wordAux.substring(idx+1,wordAux.length()); 
						wordRV = wordAux.substring(0, idx+1);
						process = true;
					}
				}
				//otherwise RV is the end of the word if these positions cannot be found.
				if (!process) {
					RV = wordAux.substring(wordAux.length()-2,wordAux.length()); 
					wordRV = wordAux.substring(0, wordAux.length()-2);
				}
			}
			
		} catch (Exception exc) {
			throw new Exception ("Context: ruleV method. Error: "+exc.getMessage());
		}
	}
	
	/**
	 * Delete the longest suffix found at pList from R1.
	 * @param pList list of suffixes.
	 * @param pContext context used for debug.
	 * @return true if the R1 content was changed.
	 * @throws Exception
	 */
	public boolean deleteR1(String[] pList, String pContext) throws Exception {
		try {
			int ctr = findLongest(pList, getR1()+getR2());
			if (ctr != -1) { 
				setR1(delete(getR1()+getR2(),pList[ctr]));
				setR2("");
				pr12 = true;
				setWordRV(getWord()); setRV(getR1()+getR2());
				if (log) System.out.println(pContext);
				return true;
			}
		} catch (Exception exc) {
			throw new Exception("Content: deleteR1 method. Error: "+exc.getMessage());
		}
		return false;
	}

	/**
	 * Delete the longest suffix found at pList from R1.
	 * @param pList list of suffixes.
	 * @param pContext context used for debug.
	 * @return true if the R2 content was changed.
	 * @throws Exception
	 */
	public boolean deleteR2(String[] pList, String pContext) throws Exception {
		try {
			int ctr = findLongest(pList, getR2());
			if (ctr != -1) { 
				setR2(delete(getR2(),pList[ctr])); 
				pr12 = true;
				setWordRV(getWord()); setRV(getR1()+getR2());
				if (log) System.out.println(pContext);
				return true;
			}
		} catch (Exception exc) {
			throw new Exception("Content: deleteR2 method. Error: "+exc.getMessage());
		}
		return false;
	}
	
	/**
	 * Replace the longest suffix found at pList by the pReplace parameter.
	 * @param pList list of suffixes.
	 * @param pReplace suffix to be replaced.
	 * @param pContext context used for debug.
	 * @return true if the RV content was changed.
	 * @throws Exception
	 */
	public boolean replaceR2(String[] pList, String pReplace, String pContext) throws Exception {
		try {
			int ctr = findLongest(pList, getR2());
			if (ctr != -1) { 
				setR2(delete(getR2(),pList[ctr])); 
				setR2(getR2()+pReplace); 
				pr12 = true;
				setWordRV(getWord()); setRV(getR1()+getR2());
				if (log) System.out.println(pContext);
				return true;
			}
		} catch (Exception exc) {
			throw new Exception("Content: replaceR2 method. Error: "+exc.getMessage());
		}
		return false;
	}
	
	/**
	 * Delete the longest suffix found at pList from RV.
	 * @param pList list of suffixes.
	 * @param pContext context used for debug.
	 * @return true if the RV content was changed.
	 * @throws Exception
	 */
	public boolean deleteRV(String[] pList, String pContext) throws Exception {
		try {
			int ctr = findLongest(pList, getRV());
			if (ctr != -1) {
				setRV(delete(getRV(),pList[ctr])); 
				setPrv(true); setPr12(false); 
				if (log) System.out.println(pContext);
				return true;
			}
		} catch (Exception exc) {
			throw new Exception("Content: deleteR2 method. Error: "+exc.getMessage());
		}
		return false;
	}
	
	/**
	 * Delete the longest suffix found at pList if the complement is also found
	 * before the suffix.
	 * @param pList list of suffixes.
	 * @param pCompl complement that should be test before the suffix in order to
	 * delete it.
	 * @param pContext context used for debug.
	 * @return true if the RV content was changed.
	 * @throws Exception
	 */
	public boolean deleteRVCompl(String[] pList, String pCompl, String pContext) throws Exception {
		try {
			int ctr = findLongest(pList, getRV());
			if (ctr != -1) {
				if ((getRV().endsWith(pCompl+pList[ctr])) ||
						((getWordRV().endsWith(pCompl)) && 
								(getRV().equalsIgnoreCase(pList[ctr])))) {	
					setRV(delete(getRV(),pList[ctr])); 
					setPrv(true); setPr12(false); 
					if (log) System.out.println(pContext);
					return true;
				}
			}
		} catch (Exception exc) {
			throw new Exception("Content: deleteR2 method. Error: "+exc.getMessage());
		}
		return false;
	}
	
	/**
	 * Replace the longest suffix found at pList by the pReplace parameter.
	 * @param pList list of suffixes.
	 * @param pReplace suffix to be replaced.
	 * @param pContext context used for debug.
	 * @return Returns true if the RV content was changed.
	 * @throws Exception
	 */
	public boolean replaceRV(String[] pList, String pReplace, String pContext) throws Exception {
		try {
			int ctr = findLongest(pList, getRV());
			if (ctr != -1) {
				setRV(delete(getRV(),pList[ctr])); 
				setRV(getRV()+pReplace);
				setPrv(true); setPr12(false);
				if (log) System.out.println(pContext);
				return true;
			}
		} catch (Exception exc) {
			throw new Exception("Content: replaceR2 method. Error: "+exc.getMessage());
		}
		return false;
	}
	
	
}

